home *** CD-ROM | disk | FTP | other *** search
/ Shareware Overload Trio 2 / Shareware Overload Trio Volume 2 (Chestnut CD-ROM).ISO / dir37 / ms_sh23s.zip / SRC / SH5.C < prev    next >
C/C++ Source or Header  |  1994-08-26  |  28KB  |  1,539 lines

  1. /*
  2.  * MS-DOS SHELL - Lexical Scanner
  3.  *
  4.  * MS-DOS SHELL - Copyright (c) 1990,4 Data Logic Limited and Charles Forsyth
  5.  *
  6.  * This code is based on (in part) the shell program written by Charles
  7.  * Forsyth and the subsequence modifications made by Simon J. Gerraty (for
  8.  * his Public Domain Korn Shell) and is subject to the following copyright
  9.  * restrictions:
  10.  *
  11.  * 1.  Redistribution and use in source and binary forms are permitted
  12.  *     provided that the above copyright notice is duplicated in the
  13.  *     source form and the copyright notice in file sh6.c is displayed
  14.  *     on entry to the program.
  15.  *
  16.  * 2.  The sources (or parts thereof) or objects generated from the sources
  17.  *     (or parts of sources) cannot be sold under any circumstances.
  18.  *
  19.  *    $Header: /usr/users/istewart/shell/sh2.3/Release/RCS/sh5.c,v 2.13 1994/08/25 20:49:11 istewart Exp $
  20.  *
  21.  *    $Log: sh5.c,v $
  22.  *    Revision 2.13  1994/08/25  20:49:11  istewart
  23.  *    MS Shell 2.3 Release
  24.  *
  25.  *    Revision 2.12  1994/02/01  10:25:20  istewart
  26.  *    Release 2.3 Beta 2, including first NT port
  27.  *
  28.  *    Revision 2.11  1994/01/11  17:55:25  istewart
  29.  *    Release 2.3 Beta 0 patches
  30.  *
  31.  *    Revision 2.10  1993/11/09  10:39:49  istewart
  32.  *    Beta 226 checking
  33.  *
  34.  *    Revision 2.9  1993/08/25  16:03:57  istewart
  35.  *    Beta 225 - see Notes file
  36.  *
  37.  *    Revision 2.8  1993/07/02  10:21:35  istewart
  38.  *    224 Beta fixes
  39.  *
  40.  *    Revision 2.7  1993/06/14  11:00:12  istewart
  41.  *    More changes for 223 beta
  42.  *
  43.  *    Revision 2.6  1993/06/02  09:52:35  istewart
  44.  *    Beta 223 Updates - see Notes file
  45.  *
  46.  *    Revision 2.5  1993/02/16  16:03:15  istewart
  47.  *    Beta 2.22 Release
  48.  *
  49.  *    Revision 2.4  1993/01/26  18:35:09  istewart
  50.  *    Release 2.2 beta 0
  51.  *
  52.  *    Revision 2.3  1992/11/06  10:03:44  istewart
  53.  *    214 Beta test updates
  54.  *
  55.  *    Revision 2.2  1992/09/03  18:54:45  istewart
  56.  *    Beta 213 Updates
  57.  *
  58.  *    Revision 2.1  1992/07/10  10:52:48  istewart
  59.  *    211 Beta updates
  60.  *
  61.  *    Revision 2.0  1992/04/13  17:39:09  Ian_Stewartson
  62.  *    MS-Shell 2.0 Baseline release
  63.  *
  64.  *
  65.  */
  66.  
  67. #include <sys/types.h>
  68. #include <sys/stat.h>
  69. #include <stdio.h>
  70. #include <signal.h>
  71. #include <errno.h>
  72. #include <setjmp.h>
  73. #include <stdlib.h>
  74. #include <string.h>
  75. #include <fcntl.h>
  76. #include <limits.h>
  77. #include <dirent.h>
  78. #include <unistd.h>
  79. #include "sh.h"
  80.  
  81. /*
  82.  * lexical analysis and source input
  83.  */
  84.  
  85. /* we set s->str to NULLSTR instead of "", so that ungetsc() works */
  86.  
  87. static    char    nullstr [] = {0, 0};
  88. #define    NULLSTR    (nullstr + 1)
  89.  
  90. static bool        expanding_alias = FALSE;
  91. static bool        AllowMultipleAliases = FALSE;
  92.  
  93. /*
  94.  * Here document processing
  95.  */
  96.  
  97. typedef struct here {
  98.     struct here        *h_next;    /* Link to next            */
  99.     IO_Actions        *h_iop;        /* I/O options            */
  100. } Here_D;
  101.  
  102. /*
  103.  * The full list contains all the documents created during a parse.  The
  104.  * active list, contains the non-processed ones.
  105.  */
  106.  
  107.                     /* Full list header        */
  108. static Here_D        *HereListHead = (Here_D *)NULL;
  109.                     /* Active list Header        */
  110. static Here_D        *ActiveListHead = (Here_D *)NULL;
  111.  
  112.  
  113. static void F_LOCAL    ReadHereDocument (IO_Actions *iop);
  114. static void F_LOCAL    GetHereDocuments (void);
  115.  
  116. static char        *LIT_Unclosed = "here document `%s' unclosed";
  117.  
  118. /*
  119.  * Get next character functions
  120.  */
  121.  
  122. static int F_LOCAL    getsc_ (void);
  123.  
  124. /* optimized getsc_() */
  125.  
  126. #define    getsc()        ((*source->str != 0) ? (int)*(source->str++) : getsc_())
  127. #define    ungetsc()    (source->str--)
  128.  
  129. /*
  130.  * states while lexing word
  131.  */
  132.  
  133. #define    SBASE        0    /* outside any lexical constructs    */
  134. #define    SSQUOTE        1    /* inside ''                */
  135. #define    SDQUOTE        2    /* inside ""                */
  136. #define    SBRACE        3    /* inside ${}                */
  137. #define    SPAREN        4    /* inside $()                */
  138. #define    SBQUOTE        5    /* inside ``                */
  139. #define    SWORD        6    /* implicit quoting for substitute()    */
  140. #define    SDPAREN        7    /* inside (( )), implicit quoting    */
  141. #define    SDDPAREN    8    /* inside $(( )), implicit quoting    */
  142. #define    SVSUBST        9    /* inside ${ }, following the V name    */
  143. #define    SVARRAY        10    /* inside ${name [..].. }         */
  144.  
  145. /*
  146.  * Lexical analyzer
  147.  *
  148.  * tokens are not regular expressions, they are LL(1).
  149.  * for example, "${var:-${PWD}}", and "$(size $(whence ksh))".
  150.  * hence the state stack.
  151.  */
  152.  
  153. int    ScanNextToken (int LexicalControlFlags)
  154. {
  155.     int            c;
  156.     char        states [64], *statep = states;
  157.     XString        ws;        /* expandable output word    */
  158.     unsigned char    *wp;        /* output word pointer        */
  159.     char        *sp, *dp;
  160.     char        istate;
  161.     char        state;
  162.     int            c2;
  163.     static int        RecursiveAliasCount = 0;
  164.     static AliasList    *RecursiveAlias[MAX_RECURSIVEALIASES];
  165.     int            SDD_Count;    /* Parathensis counter        */
  166.     char        LexSeparator;
  167.  
  168.     if (expanding_alias)
  169.     {
  170.     expanding_alias = FALSE;
  171.  
  172.     while (RecursiveAliasCount-- > 0)
  173.         RecursiveAlias[RecursiveAliasCount]->AFlags &= ~ALIAS_EXPANDING;
  174.  
  175.     RecursiveAliasCount = 0;
  176.     }
  177.  
  178. /*
  179.  * Loop round again!
  180.  */
  181.  
  182. Again:
  183.  
  184.     wp = (unsigned char *)XCreate (&ws, 64);
  185.  
  186. /*
  187.  * Single word ?
  188.  */
  189.  
  190.     if (LexicalControlFlags & ONEWORD)
  191.     istate = SWORD;
  192.  
  193. /*
  194.  * Maths expression?
  195.  */
  196.  
  197.     else if (LexicalControlFlags & MATHS_EXPRESSION)
  198.     {
  199.     istate = SDPAREN;
  200.     *(wp)++ = WORD_ODQUOTE;
  201.     }
  202.  
  203. /*
  204.  * Normal scanning
  205.  */
  206.  
  207.     else
  208.     {
  209.     istate = SBASE;
  210.  
  211. /* Ignore white space */
  212.  
  213.     while ((c = getsc ()) == CHAR_SPACE || c == CHAR_TAB)
  214.         continue;
  215.  
  216. /* Comment? */
  217.  
  218.     if (c == CHAR_COMMENT)
  219.     {
  220.         while (((c = getsc ()) != 0) && (c != CHAR_NEW_LINE))
  221.         continue;
  222.     }
  223.  
  224.     ungetsc ();
  225.     }
  226.  
  227. /*
  228.  * Which separator test to use?
  229.  */
  230.  
  231.     LexSeparator = (char)((LexicalControlFlags & TEST_EXPRESSION)
  232.             ? (C_IFS | C_SEMICOLON)
  233.             : C_LEX1);
  234.  
  235. /*
  236.  * trailing ' ' in alias definition - Yes, allow more aliases
  237.  */
  238.  
  239.     if (AllowMultipleAliases)
  240.     {
  241.     AllowMultipleAliases = FALSE;
  242.     LexicalControlFlags |= ALLOW_ALIAS;
  243.     }
  244.  
  245. /*
  246.  * collect non-special or quoted characters to form word
  247.  */
  248.  
  249.     for (*statep = state = istate;
  250.      !(((c = getsc ()) == 0) ||
  251.        ((state == SBASE) && (CharTypes[c] & LexSeparator))); )
  252.     {
  253.     XCheck (&ws, &wp);
  254.  
  255.     switch (state)
  256.     {
  257.         case SBASE:
  258. BasicCharacterProcessing:
  259.         switch (c)
  260.         {
  261.             case CHAR_META:
  262.             c = getsc ();
  263.  
  264.             if (c != CHAR_NEW_LINE)
  265.             {
  266.                 *(wp)++ = WORD_QTCHAR;
  267.                 *(wp)++ = (unsigned char)c;
  268.             }
  269.  
  270.             else if (wp == XStart (ws))
  271.                 goto Again;
  272.  
  273.             break;
  274.  
  275.             case CHAR_SINGLE_QUOTE:
  276.             *++statep = state = SSQUOTE;
  277.             *(wp)++ = WORD_OQUOTE;
  278.             break;
  279.  
  280.             case CHAR_DOUBLE_QUOTE:
  281.             *++statep = state = SDQUOTE;
  282.             *(wp)++ = WORD_ODQUOTE;
  283.             break;
  284.  
  285.             default:
  286.             goto Subst;
  287.         }
  288.  
  289.         break;
  290.  
  291. /* Non-special character processing */
  292.  
  293. Subst:
  294.         switch (c)
  295.         {
  296.  
  297. /*
  298.  * Escaped character
  299.  */
  300.             case CHAR_META:
  301.             switch (c = getsc ())
  302.             {
  303.                 case CHAR_NEW_LINE:
  304.                 break;
  305.  
  306.                 case CHAR_DOUBLE_QUOTE:
  307.                 case CHAR_META:
  308.                 case CHAR_VARIABLE:
  309.                 case CHAR_BACKQUOTE:
  310.                 *(wp)++ = WORD_QTCHAR;
  311.                 *(wp)++ = (unsigned char)c;
  312.                 break;
  313.  
  314.                 default:
  315.                 XCheck (&ws, &wp);
  316.                 *(wp)++ = WORD_CHAR;
  317.                 *(wp)++ = CHAR_META;
  318.                 *(wp)++ = WORD_CHAR;
  319.                 *(wp)++ = (unsigned char)c;
  320.                 break;
  321.             }
  322.  
  323.             break;
  324.  
  325. /*
  326.  * Handler $(), $(()), ${}, $var, $num, $#, $*, $@
  327.  */
  328.  
  329.             case CHAR_VARIABLE:
  330.             c = getsc ();
  331.  
  332. /* Check for $() & $(()) */
  333.  
  334.             if (c == CHAR_OPEN_PARATHENSIS)
  335.             {
  336.                 if (getsc () == CHAR_OPEN_PARATHENSIS)
  337.                 {
  338.                 *++statep = state = SDDPAREN;
  339.                 *(wp)++ = WORD_OMATHS;
  340.                 SDD_Count = 0;
  341.                 }
  342.  
  343.                 else
  344.                 {
  345.                 ungetsc ();
  346.                 *++statep = state = SPAREN;
  347.                 *(wp)++ = WORD_COMSUB;
  348.                 }
  349.             }
  350.  
  351. /* Check for ${} */
  352.  
  353.             else if (c == CHAR_OPEN_BRACES)
  354.             {
  355.                 *++statep = state = SVSUBST;
  356.                 *(wp)++ = WORD_OSUBST;
  357.                 c = getsc ();
  358.  
  359.                 if (!IS_VariableFC (c) && !IS_VarNumeric (c))
  360.                 ShellErrorMessage ("invalid variable name");
  361.  
  362.                 do
  363.                 {
  364.                 XCheck (&ws, &wp);
  365.                 *(wp)++ = (unsigned char)c;
  366.                 c = getsc ();
  367.                 } while (IS_VariableSC (c));
  368.  
  369.                 *(wp)++ = 0;
  370.                 ungetsc ();
  371.             }
  372.  
  373. /* Check for $var */
  374.  
  375.             else if (IS_VariableFC (c))
  376.             {
  377.                 *(wp)++ = WORD_OSUBST;
  378.  
  379.                 do
  380.                 {
  381.                 XCheck (&ws, &wp);
  382.                 *(wp)++ = (unsigned char)c;
  383.                 c = getsc ();
  384.                 } while (IS_VariableSC(c));
  385.  
  386.                 *(wp)++ = 0;
  387.                 *(wp)++ = WORD_CSUBST;
  388.                 ungetsc ();
  389.  
  390.             }
  391.  
  392. /* Check for $number, $*, $@, $#, $! $$ $- $? */
  393.  
  394.             else if (IS_VarNumeric (c))
  395.             {
  396.                 XCheck (&ws, &wp);
  397.                 *(wp)++ = WORD_OSUBST;
  398.                 *(wp)++ = (unsigned char)c;
  399.                 *(wp)++ = 0;
  400.                 *(wp)++ = WORD_CSUBST;
  401.             }
  402.  
  403. /* $??? - no mapping */
  404.  
  405.             else
  406.             {
  407.                 *(wp)++ = WORD_CHAR;
  408.                 *(wp)++ = CHAR_VARIABLE;
  409.                 ungetsc ();
  410.             }
  411.  
  412.             break;
  413.  
  414. /*
  415.  *  Handler ` ` (old style $())
  416.  */
  417.             case CHAR_BACKQUOTE:
  418.             *++statep = state = SBQUOTE;
  419.             *(wp)++ = WORD_COMSUB;
  420.             break;
  421.  
  422. /*
  423.  * Normal character
  424.  */
  425.  
  426.             default:
  427.             *(wp)++ = WORD_CHAR;
  428.             *(wp)++ = (unsigned char)c;
  429.         }
  430.  
  431.         break;
  432.  
  433.         case SSQUOTE:            /* Inside '...'        */
  434.         if (c == CHAR_SINGLE_QUOTE)
  435.         {
  436.             state = *--statep;
  437.             *(wp)++ = WORD_CQUOTE;
  438.         }
  439.  
  440. /* Check for an escaped '.  None of the UNIX versions seem to do this, so
  441.  * it may be wrong, so I've #if it out
  442.  */
  443.  
  444.         else
  445.         {
  446.             *(wp)++ = WORD_QCHAR;
  447.             *(wp)++ = (unsigned char)c;
  448.         }
  449.  
  450. #if 0
  451.         else if (c != CHAR_META)
  452.         {
  453.             *(wp)++ = WORD_QCHAR;
  454.             *(wp)++ = (unsigned char)c;
  455.         }
  456.  
  457. /* Yes - insert a quoted ' */
  458.         else if ((c = getsc ()) == CHAR_SINGLE_QUOTE)
  459.         {
  460.             *(wp)++ = WORD_QTCHAR;
  461.             *(wp)++ = CHAR_SINGLE_QUOTE;
  462.         }
  463.  
  464. /* No - unget the character and insert the meta */
  465.  
  466.         else
  467.         {
  468.             ungetsc ();
  469.             *(wp)++ = WORD_QCHAR;
  470.             *(wp)++ = CHAR_META;
  471.         }
  472. #endif
  473.  
  474.         break;
  475.  
  476.         case SDQUOTE:            /* Inside "..."        */
  477.         if (c == CHAR_DOUBLE_QUOTE)
  478.         {
  479.             state = *--statep;
  480.             *(wp)++ = WORD_CDQUOTE;
  481.         }
  482.  
  483.         else
  484.             goto Subst;
  485.  
  486.         break;
  487.  
  488.         case SPAREN:            /* Inside $(...)    */
  489.         if (c == CHAR_OPEN_PARATHENSIS)
  490.             *++statep = state;
  491.  
  492.         else if (c == CHAR_CLOSE_PARATHENSIS)
  493.             state = *--statep;
  494.  
  495.         if (state == SPAREN)
  496.             *(wp)++ = (unsigned char)c;
  497.  
  498.         else
  499.             *(wp)++ = 0; /* end of WORD_COMSUB */
  500.  
  501.         break;
  502.  
  503.         case SBRACE:            /* Inside ${...}    */
  504.         if (c != CHAR_CLOSE_BRACES)
  505.             goto BasicCharacterProcessing;
  506.  
  507.         state = *--statep;
  508.         *(wp)++ = WORD_CSUBST;
  509.         break;
  510.  
  511.         case SVARRAY:            /* Inside ${name [...].}*/
  512.         if (c != CHAR_CLOSE_BRACKETS)
  513.             goto BasicCharacterProcessing;
  514.  
  515.         state = *--statep;
  516.         *(wp)++ = WORD_CARRAY;
  517.         break;
  518.  
  519.  
  520.         case SVSUBST:        /* Inside ${ }, following the     */                        /* Variable name        */
  521.  
  522. /* Set state to SBRACE to handle closing brace. */
  523.  
  524.         *statep = state = SBRACE;
  525.  
  526. /* Simple Name
  527.  *
  528.  * Possibilities: ${name}
  529.  */
  530.  
  531.         if (c == CHAR_CLOSE_BRACES)
  532.             ungetsc ();
  533.  
  534. /*
  535.  * Array Name.  Reset to this state for closing ] post processing
  536.  *
  537.  * Possibilities: ${name[index].....}
  538.  */
  539.  
  540.         else if (c == CHAR_OPEN_BRACKETS)
  541.         {
  542.             *statep = SVSUBST;
  543.             *++statep = state = SVARRAY;
  544.             *(wp)++ = WORD_OARRAY;
  545.         }
  546.  
  547. /* Korn pattern trimming
  548.  *
  549.  * Possibilities: ${name#string}
  550.  *          ${name##string}
  551.  *          ${name%string}
  552.  */
  553.  
  554.         else if ((c == CHAR_MATCH_START) || (c == CHAR_MATCH_END))
  555.         {
  556.             if (getsc () == c)
  557.             c |= CHAR_MAGIC;
  558.  
  559.             else
  560.             ungetsc ();
  561.  
  562.             *(wp)++ = (unsigned char)c;
  563.         }
  564.  
  565. /* Subsitution
  566.  *
  567.  * Possibilities: ${name:?value}
  568.  *           ${name:-value}
  569.  *           ${name:=value}
  570.  *           ${name:+value}
  571.  *          ${name?value}
  572.  *           ${name-value}
  573.  *           ${name=value}
  574.  *           ${name+value}
  575.  */
  576.  
  577.         else
  578.         {
  579.             c2 = (c == ':') ? CHAR_MAGIC : 0;
  580.  
  581.             if (c == ':')
  582.             c = getsc();
  583.  
  584.             if (!IS_VarOp (c))
  585.             {
  586.             CompilingError ();
  587.             ShellErrorMessage ("Unknown substitution operator '%c'",
  588.                        c);
  589.             }
  590.  
  591.             *(wp)++ = (unsigned char)(c | c2);
  592.         }
  593.  
  594.         break;
  595.  
  596.         case SBQUOTE:            /* Inside `...`        */
  597.         if (c == CHAR_BACKQUOTE)
  598.         {
  599.             *(wp)++ = 0;
  600.             state = *--statep;
  601.         }
  602.  
  603.         else if (c == CHAR_META)
  604.         {
  605.             switch (c = getsc())
  606.             {
  607.             case CHAR_NEW_LINE:
  608.                 break;
  609.  
  610.             case CHAR_META:
  611.             case CHAR_VARIABLE:
  612.             case CHAR_BACKQUOTE:
  613.                 *(wp)++ = (unsigned char)c;
  614.                 break;
  615.  
  616.             default:
  617.                 *(wp)++ = CHAR_META;
  618.                 *(wp)++ = (unsigned char)c;
  619.                 break;
  620.             }
  621.         }
  622.  
  623.         else
  624.             *(wp)++ = (unsigned char)c;
  625.  
  626.         break;
  627.  
  628.         case SWORD:                /* ONEWORD        */
  629.         goto Subst;
  630.  
  631.         case SDPAREN:            /* Include ((....))    */
  632.         if (c == CHAR_CLOSE_PARATHENSIS)
  633.         {
  634.             if (getsc () == CHAR_CLOSE_PARATHENSIS)
  635.             {
  636.             *(wp)++ = WORD_ODQUOTE;
  637.             c = 0;
  638.             goto Done;
  639.             }
  640.  
  641.             else
  642.             ungetsc ();
  643.         }
  644.  
  645.         goto Subst;
  646.  
  647.         case SDDPAREN:            /* Include $((....))    */
  648.  
  649. /* Check for sub-expressions */
  650.  
  651.         if (c == CHAR_OPEN_PARATHENSIS)
  652.             SDD_Count++;
  653.  
  654. /* Check for end of sub-expression or $((..)) */
  655.  
  656.         else if (c == CHAR_CLOSE_PARATHENSIS)
  657.         {
  658.             if (SDD_Count)
  659.             SDD_Count--;
  660.  
  661.             else if (getsc () == CHAR_CLOSE_PARATHENSIS)
  662.             {
  663.             state = *--statep;
  664.             *(wp)++ = 0;
  665.             break;
  666.             }
  667.  
  668.             else
  669.             {
  670.             CompilingError ();
  671.             ShellErrorMessage ("maths sub-expression not closed");
  672.             }
  673.         }
  674.  
  675. /* Normal character? */
  676.  
  677.         *(wp)++ = (char)c;
  678.         break;
  679.     }
  680.     }
  681.  
  682. /* Finally, the end! */
  683.  
  684. Done:
  685.     XCheck (&ws, &wp);
  686.  
  687.     if (state != istate)
  688.     {
  689.     CompilingError ();
  690.     ShellErrorMessage ("no closing quote");
  691.     }
  692.  
  693.     if (!(LexicalControlFlags & TEST_EXPRESSION) &&
  694.      ((c == CHAR_INPUT) || (c == CHAR_OUTPUT)))
  695.     {
  696.     unsigned char *cp = XStart (ws);
  697.  
  698. /* Set c2 to the io unit value */
  699.  
  700.     if ((wp > cp) && (cp[0] == WORD_CHAR) && (IS_Numeric (cp[1])))
  701.     {
  702.         wp = cp; /* throw away word */
  703.         c2 = cp[1] - '0';
  704.     }
  705.  
  706.     else
  707.         c2 = (c == CHAR_OUTPUT) ? 1 : 0;    /* 0 for <, 1 for > */
  708.     }
  709.  
  710. /* no word, process LEX1 character */
  711.  
  712.     if ((wp == XStart (ws)) && (state == SBASE))
  713.     {
  714.     XFree (ws);    /* free word */
  715.     
  716.     switch (c)
  717.     {
  718.         default:
  719.         return c;
  720.  
  721.     /* Check for double character thingys - ||, &&, ;; */
  722.  
  723.         case CHAR_PIPE:
  724.         case CHAR_ASYNC:
  725.         case CHAR_SEPARATOR:
  726.         if ((c2 = getsc ()) == c)
  727.         {
  728.             switch (c)
  729.             {
  730.             case CHAR_PIPE:
  731.                 c = PARSE_LOGICAL_OR;
  732.                 break;
  733.  
  734.             case CHAR_ASYNC:
  735.                 c = PARSE_LOGICAL_AND;
  736.                 break;
  737.  
  738.             case CHAR_SEPARATOR:
  739.                 c = PARSE_BREAK;
  740.                 break;
  741.  
  742.             default:
  743.                 c = YYERRCODE;
  744.                 break;
  745.             }
  746.         }
  747.  
  748. /* Check for co-process |& */
  749.  
  750.         else if ((c == CHAR_PIPE) && (c2 == CHAR_ASYNC))
  751.             c = PARSE_COPROCESS;
  752.  
  753.         else
  754.             ungetsc ();
  755.  
  756.         return c;
  757.  
  758.     /* Re-direction */
  759.  
  760.         case CHAR_INPUT:
  761.         case CHAR_OUTPUT:
  762.         {
  763.         IO_Actions    *iop;
  764.  
  765.         iop = (IO_Actions *) GetAllocatedSpace (sizeof (IO_Actions));
  766.         iop->io_unit = c2/*unit*/;
  767.  
  768.     /* Look at the next character */
  769.  
  770.         c2 = getsc ();
  771.  
  772.     /* Check for >>, << & <> */
  773.  
  774.         if ((c2 == CHAR_OUTPUT) || (c2 == CHAR_INPUT) )
  775.         {
  776.             iop->io_flag = (c != c2) ? IORDWR
  777.                          : (c == CHAR_OUTPUT) ? IOCAT
  778.                                   : IOHERE;
  779.             c2 = getsc ();
  780.         }
  781.  
  782.         else
  783.             iop->io_flag = (c == CHAR_OUTPUT) ? IOWRITE : IOREAD;
  784.  
  785.     /* Check for <<- - strip tabs */
  786.  
  787.         if (iop->io_flag == IOHERE)
  788.         {
  789.             if (c2 == '-')
  790.             iop->io_flag |= IOSKIP;
  791.  
  792.             else
  793.             ungetsc ();
  794.         }
  795.  
  796.     /* Check for <& or >& */
  797.  
  798.         else if (c2 == '&')
  799.             iop->io_flag = IODUP;
  800.  
  801.     /* Check for >! */
  802.  
  803.         else if ((c2 == CHAR_PIPE) && (iop->io_flag == IOWRITE))
  804.             iop->io_flag |= IOCLOBBER;
  805.  
  806.         else
  807.             ungetsc ();
  808.  
  809.         yylval.iop = iop;
  810.         return PARSE_REDIR;
  811.         }
  812.  
  813.         case CHAR_NEW_LINE:
  814.         GetHereDocuments ();
  815.  
  816.         if (LexicalControlFlags & ALLOW_CONTINUATION)
  817.             goto Again;
  818.  
  819.         return c;
  820.  
  821.         case CHAR_OPEN_PARATHENSIS:
  822.         c2 = getsc();
  823.  
  824.         if (c2 == CHAR_CLOSE_PARATHENSIS)
  825.             c = PARSE_MPAREN;
  826.  
  827.         else if (c2 == CHAR_OPEN_PARATHENSIS)
  828.             c = PARSE_MDPAREN;
  829.  
  830.         else
  831.             ungetsc();
  832.  
  833.         case CHAR_CLOSE_PARATHENSIS:
  834.         return c;
  835.     }
  836.     }
  837.  
  838. /* Must be a word !!! */
  839.  
  840.     *(wp)++ = WORD_EOS;        /* terminate word */
  841.     yylval.cp = XClose (&ws, wp);
  842.  
  843.     if (state == SWORD || state == SDPAREN)    /* ONEWORD? */
  844.     return PARSE_WORD;
  845.  
  846.     ungetsc ();        /* unget terminator */
  847.  
  848. /* Make sure the CurrentLexIdentifier array stays '\0' padded */
  849.  
  850.     memset (CurrentLexIdentifier, 0, IDENT);
  851.  
  852. /* copy word to unprefixed string CurrentLexIdentifier */
  853.  
  854.     for (sp = yylval.cp, dp = CurrentLexIdentifier;
  855.      (dp < CurrentLexIdentifier + IDENT) && ((c = *sp++) == WORD_CHAR); )
  856.         *dp++ = *sp++;
  857.  
  858.     if (c != WORD_EOS)
  859.     *CurrentLexIdentifier = 0;    /* word is not unquoted */
  860.  
  861. /* Are we allowing Keywords and Aliases ? */
  862.  
  863.     if (*CurrentLexIdentifier != 0 &&
  864.     (LexicalControlFlags & (ALLOW_KEYWORD | ALLOW_ALIAS)))
  865.     {
  866.     AliasList    *p;
  867.     int        yval;
  868.     Source        *s;
  869.  
  870. /* Check keyword */
  871.  
  872.     if ((LexicalControlFlags & ALLOW_KEYWORD) &&
  873.         (yval = LookUpSymbol (CurrentLexIdentifier)))
  874.     {
  875.         ReleaseMemoryCell (yylval.cp);
  876.         return yval;
  877.     }
  878.  
  879. /* Check Alias */
  880.  
  881.     if ((LexicalControlFlags & ALLOW_ALIAS) &&
  882.         ((p = LookUpAlias (CurrentLexIdentifier, TRUE)) !=
  883.          (AliasList *)NULL) &&
  884.         (!(p->AFlags & ALIAS_EXPANDING)))
  885.     {
  886.         if (RecursiveAliasCount == MAX_RECURSIVEALIASES)
  887.         {
  888.         CompilingError ();
  889.         ShellErrorMessage ("excessive recusrsive aliasing");
  890.         }
  891.  
  892.         else
  893.         RecursiveAlias[RecursiveAliasCount++] = p;
  894.  
  895.         p->AFlags |= ALIAS_EXPANDING;
  896.  
  897. /* check for recursive aliasing */
  898.  
  899.         for (s = source; s->type == SALIAS; s = s->next)
  900.         {
  901.         if (s->u.Calias == p)
  902.             return PARSE_WORD;
  903.         }
  904.  
  905.         ReleaseMemoryCell ((void *)yylval.cp);
  906.  
  907. /* push alias expansion */
  908.  
  909.         s = pushs (SALIAS);
  910.         s->str = p->value;
  911.         s->u.Calias = p;
  912.         s->next = source;
  913.         source = s;
  914.         goto Again;
  915.     }
  916.     }
  917.  
  918.     return PARSE_WORD;
  919. }
  920.  
  921. /*
  922.  * read "<<word" text into temp file
  923.  */
  924.  
  925. static void F_LOCAL ReadHereDocument (struct ioword *iop)
  926. {
  927.     FILE    *FP;
  928.     int        tf;
  929.     int        c;
  930.     char    *EoFString;
  931.     char    *cp;
  932.     char    *Line;
  933.     size_t    LineLen;
  934.  
  935. /* Expand the terminator */
  936.  
  937.     LineLen = strlen (EoFString = ExpandAString (iop->io_name, 0));
  938.  
  939. /* Save the tempoary file information */
  940.  
  941.     iop->io_name = StringCopy (GenerateTemporaryFileName ());
  942.  
  943. /* Create the tempoary file */
  944.  
  945.     if (((tf = S_open (FALSE, iop->io_name, O_CMASK | O_NOINHERIT)) < 0)
  946.     || ((FP = ReOpenFile (tf, sOpenWriteBinaryMode)) == (FILE *)NULL))
  947.     ShellErrorMessage ("Cannot create temporary file");
  948.  
  949. /* Get some space */
  950.  
  951.     Line = GetAllocatedSpace (LineLen + 3);
  952.  
  953. /* Read the file */
  954.  
  955.     while (TRUE)
  956.     {
  957.     cp = Line;
  958.  
  959. /* Read the first n characters to check for terminator */
  960.  
  961.     while ((c = getsc ()) != CHAR_NEW_LINE)
  962.     {
  963.         if (c == 0)
  964.         ShellErrorMessage (LIT_Unclosed, EoFString);
  965.  
  966.         if (cp > Line + LineLen)
  967.         break;
  968.  
  969. /* Ignore leading tabs if appropriate */
  970.  
  971.         if (!((iop->io_flag & IOSKIP) && (cp == Line) && (c == CHAR_TAB)))
  972.         *(cp++) = (char)c;
  973.     }
  974.  
  975.     *cp = 0;
  976.     ungetsc ();
  977.  
  978. /* Check for end of string */
  979.  
  980.     if (strcmp (EoFString, Line) == 0 || c == 0)
  981.         break;
  982.  
  983. /* Dump the line */
  984.  
  985.     fputs (Line, FP);
  986.  
  987.     while ((c = getsc ()) != CHAR_NEW_LINE)
  988.     {
  989.         if (c == 0)
  990.         ShellErrorMessage (LIT_Unclosed, EoFString);
  991.  
  992.         putc (c, FP);
  993.     }
  994.  
  995.     putc (c, FP);
  996.     }
  997.  
  998.     S_fclose (FP, TRUE);
  999. }
  1000.  
  1001. /*
  1002.  * Handle scanning error
  1003.  */
  1004.  
  1005. void    CompilingError (void)
  1006. {
  1007.     yynerrs++;
  1008.  
  1009.     while (source->type == SALIAS) /* pop aliases */
  1010.     source = source->next;
  1011.  
  1012.     source->str = NULLSTR;    /* zap pending input */
  1013. }
  1014.  
  1015. /*
  1016.  * input for ScanNextToken with alias expansion
  1017.  */
  1018.  
  1019. Source *pushs (int type)
  1020. {
  1021.     Source *s = (Source *) GetAllocatedSpace (sizeof (Source));
  1022.  
  1023.     s->type = type;
  1024.     s->str = NULLSTR;
  1025.     return s;
  1026. }
  1027.  
  1028.  
  1029. /*
  1030.  * Get the next string from input source
  1031.  */
  1032.  
  1033. static int F_LOCAL getsc_ (void)
  1034. {
  1035.     Source    *s = source;
  1036.     int        c;
  1037.  
  1038.     while ((c = *s->str++) == 0)
  1039.     {
  1040.     s->str = NULL;        /* return 0 for EOF by default */
  1041.  
  1042.     switch (s->type)
  1043.     {
  1044.         case SEOF:
  1045.         s->str = NULLSTR;
  1046.         return 0;
  1047.  
  1048.         case STTY:
  1049.         s->line++;
  1050.         s->str = ConsoleLineBuffer;
  1051.         s->str[c = GetConsoleInput ()] = 0;
  1052.  
  1053.         FlushStreams ();
  1054.  
  1055. /* EOF? */
  1056.         if (c == 0)
  1057.         {
  1058.             s->str = NULL;
  1059.             s->line--;
  1060.         }
  1061.  
  1062. /* Ignore pre-white space */
  1063.  
  1064.         else
  1065.         {
  1066.             c = 0;
  1067.  
  1068.             while (s->str[c] && IS_IFS ((int)s->str[c]))
  1069.             c++;
  1070.  
  1071. /* Blank line?? */
  1072.  
  1073.             if (!s->str[c])
  1074.             {
  1075.             s->str = &s->str[c - 1];
  1076.             s->line--;
  1077.             }
  1078.  
  1079.             else
  1080.             s->str = &s->str[c];
  1081.         }
  1082.  
  1083.         break;
  1084.  
  1085.         case SFILE:
  1086.         s->line++;
  1087.         s->str = fgets (e.line, LINE_MAX, s->u.file);
  1088.  
  1089.         DPRINT (1, ("getsc_: File line = <%s>", s->str));
  1090.  
  1091.         if (s->str == NULL)
  1092.         {
  1093.             if (s->u.file != stdin)
  1094.             S_fclose (s->u.file, TRUE);
  1095.         }
  1096.  
  1097.         break;
  1098.  
  1099.         case SWSTR:
  1100.         break;
  1101.  
  1102.         case SSTRING:
  1103.         s->str = LIT_NewLine;
  1104.         s->type = SEOF;
  1105.         break;
  1106.  
  1107.         case SWORDS:
  1108.         s->str = *s->u.strv++;
  1109.         s->type = SWORDSEP;
  1110.         break;
  1111.  
  1112.         case SWORDSEP:
  1113.         if (*s->u.strv == NULL)
  1114.         {
  1115.             s->str = LIT_NewLine;
  1116.             s->type = SEOF;
  1117.         }
  1118.  
  1119.         else
  1120.         {
  1121.             s->str = " ";
  1122.             s->type = SWORDS;
  1123.         }
  1124.  
  1125.         break;
  1126.  
  1127.         case SALIAS:
  1128.         s->str = s->u.Calias->value;
  1129.  
  1130. /*
  1131.  * If there is a trailing ' ', allow multiple aliases
  1132.  */
  1133.  
  1134.         if ((*(s->str) != 0) &&
  1135.             (((c = s->str[strlen (s->str) - 1]) == CHAR_SPACE) ||
  1136.              (c == CHAR_TAB)))
  1137.             AllowMultipleAliases = TRUE;
  1138.  
  1139.         source = s = s->next;
  1140.         expanding_alias = TRUE;
  1141.         continue;
  1142.     }
  1143.  
  1144.     if (s->str == (char *)NULL)
  1145.     {
  1146.         s->type = SEOF;
  1147.         s->str = NULLSTR;
  1148.         return 0;
  1149.     }
  1150.  
  1151.     if (s->echo)
  1152.         foputs (s->str);
  1153.     }
  1154.  
  1155.     return c;
  1156. }
  1157.  
  1158. /*
  1159.  * Close all file handlers
  1160.  */
  1161.  
  1162. void CloseAllHandlers (void)
  1163. {
  1164.     int        u;
  1165.  
  1166. /*
  1167.  * Close any stream IO blocks
  1168.  */
  1169.  
  1170.     for (u = 0; u < MaxNumberofFDs; u++)
  1171.     {
  1172. #if  defined (__TURBOC__)
  1173.     if ((_streams[u].flags & _F_RDWR) && (_streams[u].fd >= NUFILE))
  1174.         fclose (&_streams[u]);
  1175. #elif  defined (__WATCOMC__)
  1176.     if ((u < _NFILES) && (__iob[u]._flag & (_READ | _WRITE)) &&
  1177.         (__iob[u]._handle >= NUFILE))
  1178.         fclose (&__iob[u]);
  1179. #elif (OS_TYPE == OS_UNIX)
  1180. #  ifdef __STDC__
  1181.     if ((__iob[u]._flag & (_IOREAD | _IORW | _IOWRT)) &&
  1182.         (__iob[u]._file >= NUFILE))
  1183.         fclose (&__iob[u]);
  1184. #  else
  1185.     if ((_iob[u]._flag & (_IOREAD | _IORW | _IOWRT)) &&
  1186.         (_iob[u]._file >= NUFILE))
  1187.         fclose (&_iob[u]);
  1188. #  endif
  1189. #elif defined (__EMX__)
  1190.     if ((_streamv[u].flags & (_IOREAD | _IORW | _IOWRT)) &&
  1191.         (_streamv[u].handle >= NUFILE))
  1192.         fclose (&_streamv[u]);
  1193. #elif !defined (__OS2__)
  1194.     if ((_iob[u]._flag & (_IOREAD | _IORW | _IOWRT)) &&
  1195.         (_iob[u]._file >= NUFILE))
  1196.         fclose (&_iob[u]);
  1197. #endif
  1198.     }
  1199.  
  1200. /* Close any open files */
  1201.  
  1202.     for (u = NUFILE; u < MaxNumberofFDs;)
  1203.     S_close (u++, TRUE);
  1204. }
  1205.  
  1206.  
  1207. /*
  1208.  * remap fd into Shell's fd space
  1209.  */
  1210.  
  1211. int ReMapIOHandler (int fd)
  1212. {
  1213.     int        i;
  1214.     int        n_io = 0;
  1215.     int        map [NUFILE];
  1216.     int        o_fd = fd;
  1217.  
  1218.     if (fd < FDBASE)
  1219.     {
  1220.     do
  1221.     {
  1222.         map[n_io++] = fd;
  1223.         fd = dup (fd);
  1224.  
  1225.     } while ((fd >= 0) && (fd < FDBASE));
  1226.  
  1227.     for (i = 0; i < n_io; i++)
  1228.         close (map[i]);
  1229.  
  1230. /* Check we can map it */
  1231.  
  1232.     if (fd >= (32 + FDBASE))
  1233.     {
  1234.         close (fd);
  1235.         fd = -1;
  1236.     }
  1237.     
  1238.     else
  1239.     {
  1240.         S_Remap (o_fd, fd);
  1241.         S_close (o_fd, TRUE);
  1242.     }
  1243.  
  1244.     if (fd < 0)
  1245.     {
  1246. #ifdef DEBUG
  1247.         struct stat        st;
  1248.  
  1249.         fprintf (stderr, "\nOld Fd=%d, New=%d, Map=0x%.8lx\n", o_fd, fd,
  1250.              e.IOMap);
  1251.  
  1252.         feputs ("Ch. Device Inode  Mode  Links RDev   Size\n");
  1253.  
  1254.         for (i = 0; i < MaxNumberofFDs; i++)
  1255.         {
  1256.         if (fstat (i, &st) < 0)
  1257.             fprintf (stderr, "%2d: cannot get status (%s)\n", i,
  1258.                  strerror (errno));
  1259.  
  1260.         else
  1261.             fprintf (stderr, "%2d: 0x%.4x %5u %6o %5d 0x%.4x %ld\n", i,
  1262.                  st.st_dev, st.st_ino, st.st_mode, st.st_nlink,
  1263.                  st.st_rdev, st.st_size);
  1264.         }
  1265. #endif
  1266.         ShellErrorMessage ("too many files open");
  1267.     }
  1268.     }
  1269.  
  1270.     return fd;
  1271. }
  1272.  
  1273.  
  1274. /*
  1275.  * Generate a temporary filename
  1276.  */
  1277.  
  1278. char *GenerateTemporaryFileName (void)
  1279. {
  1280.     static char    tmpfile[FFNAME_MAX];
  1281.     char    *tmpdir;    /* Points to directory prefix of pipe    */
  1282.     static int    temp_count = 0;
  1283.     char    *sep = DirectorySeparator;
  1284.  
  1285. /* Find out where we should put temporary files */
  1286.  
  1287.     if (((tmpdir = GetVariableAsString ("TMP", FALSE)) == null) &&
  1288.     ((tmpdir = GetVariableAsString (HomeVariableName, FALSE)) == null) &&
  1289.     ((tmpdir = GetVariableAsString ("TMPDIR", FALSE)) == null))
  1290.     tmpdir = CurrentDirLiteral;
  1291.  
  1292.     if (strchr ("/\\", tmpdir[strlen (tmpdir) - 1]) != (char *)NULL)
  1293.     sep = null;
  1294.  
  1295. /* Get a unique temporary file name */
  1296.  
  1297.     while (1)
  1298.     {
  1299.     sprintf (tmpfile, "%s%ssht%.5u.tmp", tmpdir, sep, temp_count++);
  1300.  
  1301.     if (!S_access (tmpfile, F_OK))
  1302.         break;
  1303.     }
  1304.  
  1305.     return tmpfile;
  1306. }
  1307.  
  1308. /*
  1309.  * XString functions
  1310.  */
  1311.  
  1312. /*
  1313.  * Check an expandable string for overflow
  1314.  */
  1315.  
  1316. void    XCheck (XString *xs, unsigned char **xp)
  1317. {
  1318.     if (*xp >= xs->SEnd)
  1319.     {
  1320.     int    OldOffset = *xp - xs->SStart;
  1321.  
  1322.     xs->SStart = ReAllocateSpace (xs->SStart, (xs->SLength *= 2) + 8);
  1323.     xs->SEnd = xs->SStart + xs->SLength;
  1324.     *xp = xs->SStart + OldOffset;
  1325.     }
  1326. }
  1327.  
  1328. /*
  1329.  * Close an Expanded String
  1330.  */
  1331.  
  1332. char    *XClose (XString *xs, unsigned char *End)
  1333. {
  1334.     size_t    len = End - xs->SStart;
  1335.     char    *s = memcpy (GetAllocatedSpace (len), xs->SStart, len);
  1336.  
  1337.     ReleaseMemoryCell (xs->SStart);
  1338.     return s;
  1339. }
  1340.  
  1341. /*
  1342.  * Create an Expanded String
  1343.  */
  1344.  
  1345. char    *XCreate (XString *xs, size_t len)
  1346. {
  1347.     xs->SLength = len;
  1348.     xs->SStart = GetAllocatedSpace (len + 8);
  1349.     xs->SEnd = xs->SStart + len;
  1350.     return (char *)xs->SStart;
  1351. }
  1352.  
  1353. /*
  1354.  * here documents
  1355.  *
  1356.  * Save a here file's IOP for later processing (ie delete of the temp file
  1357.  * name)
  1358.  */
  1359.  
  1360. void SaveHereDocumentInfo (IO_Actions *iop)
  1361. {
  1362.     Here_D    *h, *lh;
  1363.  
  1364.     if ((h = (Here_D *) GetAllocatedSpace (sizeof(Here_D))) == (Here_D *)NULL)
  1365.     return;
  1366.  
  1367.     h->h_iop     = iop;
  1368.     h->h_next    = (Here_D *)NULL;
  1369.  
  1370.     if (HereListHead == (Here_D *)NULL)
  1371.     HereListHead = h;
  1372.  
  1373.     else
  1374.     {
  1375.     for (lh = HereListHead; lh != (Here_D *)NULL; lh = lh->h_next)
  1376.     {
  1377.         if (lh->h_next == (Here_D *)NULL)
  1378.         {
  1379.         lh->h_next = h;
  1380.         break;
  1381.         }
  1382.     }
  1383.     }
  1384. }
  1385.  
  1386. /*
  1387.  * Read all the active here documents
  1388.  */
  1389.  
  1390. static void F_LOCAL GetHereDocuments (void)
  1391. {
  1392.     Here_D    *h, *hp;
  1393.  
  1394. /* Scan here files first leaving HereListHead list in place */
  1395.  
  1396.     for (hp = h = HereListHead; h != (Here_D *)NULL; hp = h, h = h->h_next)
  1397.     ReadHereDocument (h->h_iop);
  1398.  
  1399. /* Make HereListHead list active - keep list intact for scraphere */
  1400.  
  1401.     if (hp != (Here_D *)NULL)
  1402.     {
  1403.     hp->h_next     = ActiveListHead;
  1404.     ActiveListHead     = HereListHead;
  1405.     HereListHead     = (Here_D *)NULL;
  1406.     }
  1407. }
  1408.  
  1409. /*
  1410.  * Zap all the here documents, unless they are currently in use by a
  1411.  * function.
  1412.  */
  1413.  
  1414. void ScrapHereList (void)
  1415. {
  1416.     Here_D    *h;
  1417.  
  1418.     for (h = HereListHead; h != (Here_D *)NULL; h = h->h_next)
  1419.     {
  1420.     if ((h->h_iop != (IO_Actions *)NULL) &&
  1421.         (h->h_iop->io_name != (char *)NULL) &&
  1422.         (!(h->h_iop->io_flag & IOFUNCTION)))
  1423.         unlink (h->h_iop->io_name);
  1424.     }
  1425.  
  1426.     HereListHead = (Here_D *)NULL;
  1427. }
  1428.  
  1429. /*
  1430.  * unlink here temp files before a ReleaseMemoryArea (area)
  1431.  */
  1432.  
  1433. void FreeAllHereDocuments (int area)
  1434. {
  1435.     Here_D    *current = ActiveListHead;
  1436.     Here_D    *previous = (Here_D *)NULL;
  1437.  
  1438.     while (current != (Here_D *)NULL)
  1439.     {
  1440.     if (GetMemoryAreaNumber ((void *)current) >= area)
  1441.     {
  1442.         if ((current->h_iop->io_name != (char *)NULL) &&
  1443.         (!(current->h_iop->io_flag & IOFUNCTION)))
  1444.         unlink (current->h_iop->io_name);
  1445.  
  1446.         if (ActiveListHead == current)
  1447.         ActiveListHead = current->h_next;
  1448.  
  1449.         else
  1450.         previous->h_next = current->h_next;
  1451.     }
  1452.  
  1453.     previous = current;
  1454.     current = current->h_next;
  1455.     }
  1456. }
  1457.  
  1458. /*
  1459.  * Open here document temp file.
  1460.  * If unquoted here, expand here temp file into second temp file.
  1461.  */
  1462.  
  1463. int    OpenHereFile (char *hname, bool sub)
  1464. {
  1465.     FILE    *FP;
  1466.     char    *cp;
  1467.     Source    *s;
  1468.     char    *tname = (char *)NULL;
  1469.     jmp_buf    ReturnPoint;
  1470.  
  1471. /* Check input file */
  1472.  
  1473.     if (hname == (char *)NULL)
  1474.     return -1;
  1475.  
  1476. /*
  1477.  * If processing for $, ` and ' is required, do it
  1478.  */
  1479.  
  1480.     if (sub)
  1481.     {
  1482.     CreateNewEnvironment ();
  1483.  
  1484.     if (SetErrorPoint (ReturnPoint) == 0)
  1485.     {
  1486.         if ((FP = FOpenFile (hname, sOpenReadMode)) == (FILE *)NULL)
  1487.         {
  1488.         PrintErrorMessage ("Here Document (%s) lost", hname);
  1489.         return -1;
  1490.         }
  1491.  
  1492. /* set up ScanNextToken input from here file */
  1493.  
  1494.         s = pushs (SFILE);
  1495.         s->u.file = FP;
  1496.         source = s;
  1497.  
  1498.         if (ScanNextToken (ONEWORD) != PARSE_WORD)
  1499.         PrintErrorMessage ("Here Document error");
  1500.  
  1501.         cp = ExpandAString (yylval.cp, 0);
  1502.  
  1503.     /* write expanded input to another temp file */
  1504.  
  1505.         tname = StringCopy (GenerateTemporaryFileName ());
  1506.  
  1507.         if ((FP = FOpenFile (tname, sOpenAppendMode)) == (FILE *)NULL)
  1508.         {
  1509.         ShellErrorMessage (Outofmemory1);
  1510.         TerminateCurrentEnvironment (TERMINATE_COMMAND);
  1511.         }
  1512.  
  1513.         fputs (cp, FP);
  1514.         CloseFile (FP);
  1515.  
  1516.         QuitCurrentEnvironment ();
  1517.  
  1518.         return S_open (TRUE, tname, O_RDONLY);
  1519.     }
  1520.  
  1521. /* Error - terminate */
  1522.  
  1523.     else
  1524.     {
  1525.         QuitCurrentEnvironment ();
  1526.         CloseFile (FP);
  1527.  
  1528.         if (tname != (char *)NULL)
  1529.         unlink (tname);
  1530.  
  1531.         return -1;
  1532.     }
  1533.     }
  1534.  
  1535. /* Otherwise, just open the document */
  1536.  
  1537.     return S_open (FALSE, hname, O_RDONLY);
  1538. }
  1539.